using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.UI;
using XYTerrain.Runtime;
using Debug = UnityEngine.Debug;

namespace  XYTerrain.Editor
{
    
public class TerrainSoundBakeEditorWindow :  EditorWindow
{

    [MenuItem("Tools/Nature Manufacture/XY/地形音效生成器")]
    public static void OpenWindow()
    { TerrainSoundBakeEditorWindow window = EditorWindow.GetWindow<TerrainSoundBakeEditorWindow>("地形音效烘焙");
        
    }

    public List<Terrain> terrains = new List<Terrain>();
    public TerrainSoundConfig soundConfig;
    public TerrainSoundData soundData;
    private bool singleBeak = false;
    private SerializedObject _serObj;
    private SerializedProperty _terrainpty;
    private int splitTime = 0;
    private Vector3 centerPoint;
    private int toolBarInt;
    private string sceneName = null;

    private string[] toolBarStr =
    {
        "普通地形", "Mesh地形"
    };
    
    
    
    //TODO  做压缩啊大哥
    public int compressValue = 32;

    public void OnEnable()
    {

        _serObj = new SerializedObject(this);
        _terrainpty = _serObj.FindProperty("terrains");
        Debug.Log("enable");
        
    }

    #region  Gui绘制相关

    

    
    private void OnGUI()
    {
        
        
        toolBarInt = GUILayout.Toolbar(toolBarInt, toolBarStr);
        EditorGUILayout.Space(10);

        //-------------------------------------- 
        DrawConfigs();
        
        switch (toolBarInt)
        {
           case 0 : DrawTerrainBeakGUI(); break;
           case 1 : DrawPaintMeshGUI(); break;
        } 
        
        DrawDeBugGUI();
    }


    private void GetAllSceneTerrain()
    {
       var terrainsArray = FindObjectsOfType<Terrain>();
       terrains .Clear();
       for (int i = 0; i < terrainsArray.Length; i++)
       {
          terrains.Add(terrainsArray[i]); 
       }
       OrderByCube();
       
    }


    
    
    
    
    private void DrawTerrainBeakGUI()
    {

        // EditorGUILayout.Toggle();
         
        EditorGUILayout.BeginVertical(EditorStyles.helpBox);
        EditorGUILayout.Space(10);
        if(_terrainpty!=null) EditorGUILayout.PropertyField(_terrainpty);

        
        
        EditorGUILayout.Space(5);
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("获取场景所有Terrain")) 
        {
            GetAllSceneTerrain();
        }
        
        singleBeak = EditorGUILayout.Toggle("所有地形独立烘焙",singleBeak);
        EditorGUILayout.EndHorizontal();
                
        _serObj.Update();
        EditorGUI.BeginChangeCheck();
        if (EditorGUI.EndChangeCheck())
        {
            _serObj.ApplyModifiedProperties(); 
        }
        
        EditorGUILayout.Space(10);
        EditorGUILayout.EndVertical();




        EditorGUILayout.BeginVertical(EditorStyles.helpBox);
        
        EditorGUILayout.LabelField("烘焙",EditorStyles.boldLabel);
        
        
        

        if (GUILayout.Button("音效烘焙处理"))
        {
            if(singleBeak) BeakSoundDataSingle();
            else BeakSoundData(); 
        }
        EditorGUILayout.EndVertical();
    }

    private void DrawConfigs()
    {
        EditorGUILayout.Space(10);
        EditorGUILayout.BeginVertical(EditorStyles.helpBox);
        EditorGUILayout.LabelField("配置信息");
        EditorGUILayout.LabelField("保存路径 :"+savePath,EditorStyles.helpBox);
        soundData = EditorGUILayout.ObjectField("",soundData,typeof(TerrainSoundData),true) as TerrainSoundData; 
        if (soundData != null)
        {
            if(GUILayout.Button("定位到地形音效信息文件"))
            {
                LocateToSoundData();
            }
        }
        soundConfig = EditorGUILayout.ObjectField("",soundConfig,typeof(TerrainSoundConfig),true)  as TerrainSoundConfig;
        if (soundConfig != null)
        {
            if(GUILayout.Button("定位到音效配置文件"))
            {
                LocateSoundConfig();
            }
        }
        EditorGUILayout.EndVertical();
        EditorGUILayout.Space(10);
    }

    private void LocateToSoundData()
    {
         Selection.activeObject = soundData;
    }



    #region Debug调试相关

    private bool posDebug
        {
            get => _posDebug;
            set
            {
                if (value != _posDebug)
                {
                    if (value)
                    {
                        SceneView.duringSceneGui += OnSceneGui;
                    }
                    else
                    {
                        SceneView.duringSceneGui -= OnSceneGui;
                    }
    
                    _posDebug = value;
                }
            }
        }
        
        private bool _posDebug = false;
        private Vector2 _debug_pos;
        private int _debug_soundId;
        
        private void DrawDeBugGUI()
        {
    
            posDebug = EditorGUILayout.Toggle("位置调试", posDebug);
            if (posDebug)
            {
                if (soundData == null|| soundConfig==null)
                {
                    EditorGUILayout.HelpBox("缺少音效配置火信息数据读取",MessageType.Warning);
                }
                else
                {

                    
                    EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                    EditorGUILayout.LabelField($"当前测试位置: x :{_debug_pos.x} z:{_debug_pos.y}",EditorStyles.helpBox);
                    var converPos = soundData.GetDebugPos(_debug_pos);
                    EditorGUILayout.LabelField($"转化位置: x :{converPos.x} z:{converPos.y}",EditorStyles.helpBox);
                    _debug_soundId = soundData.GetSoundId(_debug_pos);
                    EditorGUILayout.LabelField($"当前音效："+ soundConfig.nameList[_debug_soundId]);   
                    
                    EditorGUILayout.EndVertical();    
                }
                
            }

            // if (GUILayout.Button("输出预览贴图"))
            // {
            //    DebugPreviewTex(); 
            // }
    
        }


        private void OnFocus()
        {
            SceneView.duringSceneGui -= DrawDebugWire;
            SceneView.duringSceneGui += DrawDebugWire;
        }

        private void OnLostFocus()
        {
            SceneView.duringSceneGui -= DrawDebugWire;
        }

        private void DrawDebugWire(SceneView sv)
        {
            if (!posDebug) return;
            Handles.BeginGUI();
            Debug.DrawLine(soundData.centerPoint,soundData.centerPoint+new Vector3(soundData.roundSize,0,0));
            // Gizmos.DrawWireCube(soundData.centerPoint,new Vector3(soundData.roundSize,soundData.roundSize,soundData.roundSize));
            Handles.EndGUI();
            // Debug.DrawLine();    
        }
        
        
        private void DebugPreviewTex()
        {

            int size = (int)Mathf.Sqrt(soundData.soundData.Length);
             Texture2D preview = new Texture2D( size, size);
             

             Color[] colorList = new[]
             {
                 Color.black,
                 Color.red,
                 Color.green,
                 Color.blue,
                 Color.yellow,
                 Color.gray
             };

             
             for (int x = 0; x < size; x++)
             {
                 for (int y = 0; y < size; y++)
                 {
                     
                     int soundId = soundData.soundData[x+y*size];
                     preview.SetPixel(x,y,colorList[soundId% colorList.Length]);
                 }
             }
     
             var bytes  = preview.EncodeToPNG();
             string path = Application.dataPath+"/Preview.png";
             var file = System.IO.File.Open(path, System.IO.FileMode.OpenOrCreate);
             file.Write(bytes,0,bytes.Length);
             file.Close(); 
             AssetDatabase.SaveAssets();
             AssetDatabase.Refresh();
             Debug.Log("预览图生成完毕 位置 ："+path);
         
                
        }
    
        
        
        
        /// <summary>
        ///  换算有问题 暂时停用
        /// </summary>
        /// <returns></returns>
        private  Vector2 GetMousePosToScene(SceneView sceneView)
        {
            //当前屏幕坐标,左上角(0,0)右下角(camera.pixelWidth,camera.pixelHeight)
    
            if (Event.current == null)
            {
                return Vector2.zero;
            }
            Vector2 mousePos = Event.current.mousePosition;
            //retina 屏幕需要拉伸值
            float mult = 1;
    #if UNITY_5_4_OR_NEWER
            mult = EditorGUIUtility.pixelsPerPoint;
    #endif
            //转换成摄像机可接受的屏幕坐标,左下角是(0,0,0);右上角是(camera.pixelWidth,camera.pixelHeight,0)
            mousePos.y = sceneView.camera.pixelHeight - mousePos.y ;
            // mousePos.x *= mult;
            //近平面往里一些,才能看到摄像机里的位置
            Vector3 fakePoint = mousePos;
            fakePoint.z = 20;
            Vector3 point = sceneView.camera.ScreenToWorldPoint(fakePoint);
            mousePos.x = point.x;
            mousePos.y = point.z;
            
            return mousePos;
        }






        private float timer = 0;
        private void OnSceneGui(SceneView view)
        {
            timer += Time.deltaTime;
            if (timer >= 0.1f)
            {
                _debug_pos =  GetMousePosToScene(view);
                this.Repaint();
                timer = 0;
            }
            
        }
    
        

    #endregion 
    
    
    private void DrawCompressSlider()
    {
        compressValue = Mathf.NextPowerOfTwo(EditorGUILayout.IntField("最大限制音效数", compressValue));
    }
    
    
    
    
    private void DrawWarringBox()
    {
        EditorGUILayout.HelpBox("Terrain起始点需要为 (0,0)",MessageType.Warning);    
    }

    #endregion
    
    //------------------ 功能实现 -------------- 
    
    
    
   #region  音效配置加载
    
    private bool LoadSoundConfig()
    {
        if (soundConfig != null) return true;
        
        var path  = AssetDatabase.FindAssets("t:TerrainSoundConfig");
        bool loaded = false;
        if (path.Length==0 )
        {
             EditorUtility.DisplayDialog("提示", "还没生成音效配置文件！", "好的", "不好");
        }else if (path.Length == 1)
        {
            var  loadPath  = AssetDatabase.GUIDToAssetPath(path[0]);
            soundConfig = AssetDatabase.LoadAssetAtPath<TerrainSoundConfig>(loadPath);
            loaded = true;
        }else if (path.Length > 1)
        {
            EditorUtility.DisplayDialog("提示", "存在多个音效配置文件 请手动选择或清除不需要的", "好的", "不好");
        }
        return loaded;
    }

    

    private void LocateSoundConfig()
    {
        if(soundConfig!=null) Selection.activeObject = soundConfig;
    }
    
    #endregion


    private string savePath;

    /// <summary>
    /// 数据烘焙主函数
    /// </summary>
    private void BeakSoundData()
    {


        if (!LoadSoundConfig()) return;

        if (terrains.Count == 0)
        {
            EditorUtility.DisplayDialog("提示", "地形数据为空", "ok");
            return;
        }
        
        
        OrderByCube(); // 地形重排序
        
        soundConfig.WarmUpTable();
        if (!LayerCheck()) return;

        
        if (soundData == null)
        {
            if (String.IsNullOrEmpty(savePath))
            {
                savePath =  EditorUtility.SaveFilePanelInProject("","","asset","");
            }
            else
            {
                savePath =  EditorUtility.SaveFilePanelInProject("","","asset","");
            }    
        }
        
        
        
        var soundMat = GetPostMatrix();

        if (soundData == null)
        {
            soundData = ScriptableObject.CreateInstance<TerrainSoundData>();
            AssetDatabase.CreateAsset(soundData,savePath);
            
        }
        
        
        soundData.Init(soundMat,terrains[0]);
        soundData.mapId = Guid.NewGuid().ToString();
        soundData.centerPoint = centerPoint;
        
        EditorUtility.SetDirty(soundData);
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        Debug.Log("完成");



    }

    private void BeakSoundDataSingle()
    {
        if (!LoadSoundConfig()) return;
     
        if (terrains.Count == 0)
        {
            EditorUtility.DisplayDialog("提示", "地形数据为空", "ok");
            return;
        }
        soundConfig.WarmUpTable();


        if (!LayerCheck()) return;
        
        savePath = EditorUtility.SaveFilePanelInProject("保存根目录","{0}_{1}","asset","");

        // List<TerrainSoundData>();
        for (int i = 0;  i< terrains.Count; i++)
        {
            EditorUtility.DisplayProgressBar("数据生成中","数据生成第"+i+"个",terrains.Count/(1+i));
            var soundMat = GetSingleLayerSoundData(terrains[i]);

            var singleSoundData = ScriptableObject.CreateInstance<TerrainSoundData>();
            var sceneID = IndexToSceneID(i);
            string sceneIDName = $"x{sceneID.x}_z{sceneID.y}";
            sceneName = terrains[i].name;
            string path = String.Format(savePath,sceneName ,sceneIDName);
            
            singleSoundData.Init(soundMat,terrains[i]);
            singleSoundData.mapId = sceneName+sceneIDName;
            singleSoundData.centerPoint = terrains[i].transform.position;
            
            EditorUtility.SetDirty(singleSoundData);
            AssetDatabase.CreateAsset(singleSoundData,path);
        } 
        
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        Debug.Log("完成所有子场景输出"); 
        EditorUtility.ClearProgressBar();

    }
    
    
    private Vector2Int IndexToSceneID(int id)
    {
        int  m_splitTime = (int)Mathf.Sqrt(terrains.Count);
        int x = Mathf.FloorToInt( ((float)id / m_splitTime));
        int z = ((id) %(m_splitTime));
        return new Vector2Int(x, z);
    }
    



    /// <summary>
    ///  检查是否有未配置地形文件
    /// </summary>
    /// <returns></returns>
    private bool LayerCheck()
    {

        bool pass = true;
        List<string> nameList = new List<string>();
        foreach (var i in terrains)
        {
            foreach (var layer in i.terrainData.terrainLayers)
            {
                int id = soundConfig.GetSoundId(layer.GetInstanceID());
                if (id == -1)
                {
                    if(!nameList.Contains(layer.name)) nameList.Add(layer.name);
                    pass = false;
                }
            }
        }

        if (!pass)
        {
            string str = "配置文件中未对该地形进行配置:\n";
            foreach (var i in nameList)
            {
                str += i + "\n";
            }
            Debug.LogError(str);
            
            str+= "是否跳转到配置";
            bool jump = EditorUtility.DisplayDialog("警告", str, "是", "否");
            if(jump) LocateSoundConfig();
        }

        return pass;

    }
    
    private uint[,] GetPostMatrix()
    {

        
        List<uint[,]> soundMatrixList = new List<uint[,]>();

        foreach (var i in terrains){
            soundMatrixList.Add(GetSingleLayerSoundData(i)); 
        }

        int texWidth = (int) (terrains[0].terrainData.alphamapWidth);

        var globalSoundData = CombineMatrix( texWidth, soundMatrixList.ToArray());

        return globalSoundData;


    }
    
    
    
    



    /// <summaryk>
    /// 将所有soundIndex矩阵合为一个大矩阵
    /// </summary>
    /// <returns></returns>
    uint[,] CombineMatrix( int size,params uint[][,]  matrix)
    {
        uint[,] combine = new uint[size*splitTime,size*splitTime];

        for (int i = 0; i < terrains.Count; i++)
        {
            combine = CombineSingle(i,  size, combine, matrix[i]);
        }
        
        return combine;

    }






    uint[,] CombineSingle(int index, int size, uint[,] globalMatrix, uint[,] matrix)
    {

        var vecOffset = GetPositionOnIndex(size, index);
        for (int x = 0; x < matrix.GetLength(0); x++)
        {
            for (int y = 0; y < matrix.GetLength(1); y++)
            {
                globalMatrix[x + vecOffset.x, y + vecOffset.y] = matrix[x, y];
            } 
        }

        return globalMatrix;

    }

    private uint [,] GetSingleLayerSoundData(Terrain terrain)
    {
                
        var terrainData = terrain.terrainData;
        //TODO这里用alphatMapsize可能会导致后续alphaMap大小宇顶点数目不一致导致作用域出现偏差
        var alphaMatrix = terrain.terrainData.GetAlphamaps(0, 0, terrainData.alphamapWidth, terrainData.alphamapHeight);
        var soundMatrix = new uint[alphaMatrix.GetLength(0), alphaMatrix.GetLength(1)];
        var layerCount = terrainData.terrainLayers.Length; 
        var texWidth = alphaMatrix.GetLength(0);
        var texHeight = alphaMatrix.GetLength(0);

        int maxInt = 0;
        float maxFloat = 0;
        for (int width   = 0; width  < texWidth; width ++)
        {
            for (int  height = 0;  height < texHeight;  height++)
            {
                maxInt = 0;
                maxFloat = 0;

                for (int layerID = 0; layerID < layerCount; layerID++)
                {
                    if (maxFloat < alphaMatrix[height, width, layerID])
                    {
                        maxFloat = alphaMatrix[height, width, layerID];
                        maxInt = layerID;
                    }
                }
                 
                soundMatrix[width, height]=LayerToSoundID( terrainData.terrainLayers[maxInt]);
            }
        } 
        
        return soundMatrix;
    
    }
    
    uint LayerToSoundID(TerrainLayer data)
     {

         var soundId = soundConfig.GetSoundId(data.GetInstanceID());
         if (soundId != -1)
         {
             return (uint)soundId;
         }
         else
         {
             Debug.LogError("配置文件中未对该地形进行配置 : " + data.name);
             return 0;
         }
     }
    
    

    #region 将地形进行九宫格排序

    /// <summary>
        /// 根据九宫规则进行排序
        /// </summary>
        void OrderByCube()
        {
            
            splitTime = (int) Mathf.Sqrt(terrains.Count);
    
            if (splitTime % 1 != 0)
            {
                Debug.LogError("当前收集的Terrains并不是nxn排列");
                return;
            }
            
            int width = (int) terrains[0].terrainData.size.x; 
    
            Terrain swap = null;
            
            centerPoint = Vector2.zero;
            foreach (var i in terrains)
            {
                var pos = i.transform.position;
                centerPoint+= new Vector3(pos.x,0 ,pos.z);
            }

            var wide = terrains[0].terrainData.size.x;
            centerPoint /= terrains.Count;
            centerPoint -= (splitTime-1)*new Vector3(wide,0,wide)/2;            
         
            for (int i = 0; i < terrains.Count; i++)
            {
    
                var vec = GetPositionOnIndex(width,i);
                var nearlyPoint = new Vector3(centerPoint.x+vec.x,0,centerPoint.z+vec.y);
                var  nearlyID = terrains.IndexOf(GetNearlyTerrain(nearlyPoint , i));
                if (nearlyID != i)
                {
                    Debug.Log("Change");
                    swap = terrains[nearlyID];
                    terrains[nearlyID] = terrains[i];
                    terrains[i] = swap;
                }
                
            }


        }
    
    
        Terrain GetNearlyTerrain(Vector3 pos,int beginValue)
        {
            Terrain t = null;
            t = terrains[beginValue];
    
            float minDistance = (pos - t.transform.position).sqrMagnitude;
    
            for (int i = beginValue; i < terrains.Count ; i++)
            {
    
                var m_dis = (pos - terrains[i].transform.position).sqrMagnitude;
                if (m_dis < minDistance)
                {
                    t = terrains[i];
                    minDistance =  m_dis;
                }
            }
    
            return t;
        }
    
           
        
        
        Vector2Int GetPositionOnIndex(int size,int index)
        {
    
            var vec = new Vector2Int();
            int horizonOffset = Mathf.FloorToInt((float)index/splitTime)*size;
            int verticalOffset = (index % splitTime) * size;
            
            //alphamap的读取轴是xz互换的
            vec.x = verticalOffset; 
            vec.y = horizonOffset;
             
            return vec;
        }

        
       //---------------------------------BeakByMeshTerrain----------------------------------------- 
       // 通过东子哥的Mesh数据进行烘焙
       
       
          public GameObject meshRoot;
          
          public void BeakByMeshTerrain()
          {

             
              
              if (!LoadSoundConfig()) return;
              
              if(meshRoot==null) return;
              
              var paintData = meshRoot.GetComponent<PaintTerrainData>();
              if(paintData==null) return;

              soundConfig.WarmUpTable(); 
              if(!MeshLayerCheck()) return;
                 
              if (soundData == null)
              {
                  if (String.IsNullOrEmpty(savePath))
                  {
                      savePath =  EditorUtility.SaveFilePanelInProject("","","asset","");
                  }
                  else
                  {
                      savePath =  EditorUtility.SaveFilePanelInProject("","","asset","");
                  }    
                  soundData = ScriptableObject.CreateInstance<TerrainSoundData>();
                  AssetDatabase.CreateAsset(soundData,savePath);
              }
              
              
              
              var handleData = new MeshTerrianSoundData
              {
                   go = meshRoot
              };
              
                
              
       
              MeshTerrainSoundBakeTool.GetPreviewCtlTex(handleData,true);
              var result = MeshTerrainSoundBakeTool.GetSoundData(handleData,paintData,LayerToSoundID);
              soundData = ScriptableObject.CreateInstance<TerrainSoundData>();
              soundData.Init(result,handleData.bounds);
              
              
              DebugPreviewTex();
              AssetDatabase.CreateAsset(soundData,savePath);              
              AssetDatabase.SaveAssets();
              AssetDatabase.Refresh();
       
       
          }



          private void DrawPaintMeshGUI()
          {
              meshRoot = EditorGUILayout.ObjectField("Mesh地形文件", meshRoot, typeof(GameObject)) as GameObject;
              if (GUILayout.Button("烘焙"))
              {
                  BeakByMeshTerrain();
              }
          

          }
          
          private bool MeshLayerCheck()
          {
      
              bool pass = true;
              List<string> nameList = new List<string>();
              var o = meshRoot.GetComponent<PaintTerrainData>();
              foreach (var layer in o.Layers)
              {
                  int id = soundConfig.GetSoundId(layer.GetInstanceID());
                  if (id == -1)
                  {
                      if(!nameList.Contains(layer.name)) nameList.Add(layer.name);
                      pass = false;
                  }
              }
      
              if (!pass)
              {
                  string str = "配置文件中未对该地形进行配置:\n";
                  foreach (var i in nameList)
                  {
                      str += i + "\n";
                  }
                  Debug.LogError(str);
                  
                  str+= "是否跳转到配置";
                  bool jump = EditorUtility.DisplayDialog("警告", str, "是", "否");
                  if(jump) LocateSoundConfig();
              }
      
              return pass;
      
          }
          
          
        
    #endregion 
    
}





}

